package com.addrbook.repository;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.springframework.stereotype.Repository;

import au.com.bytecode.opencsv.CSVReader;

import com.addrbook.domain.AddressBook;
import com.addrbook.repository.CSVParser.Entry;

/**
 * This class is responsible for accessing address book data.
 * 
 * @author Zainul Franciscus
 * 
 */
@Repository("addressBook")
public class AddressBookRepository {

	/**
	 * The application can record many address books. Each book is associated
	 * with a unique name. This map records each book with a unique name
	 */
	public Map<String, AddressBook> addressBooks = new HashMap<String, AddressBook>();

	/**
	 * Create an address book if there is no address book with the provided
	 * name, and record the provided name and phone into the address book.
	 * 
	 * @param bookname
	 *            a unique name that can be used to identify an address book
	 * @param name
	 *            of a person
	 * @param phone
	 *            of the person
	 */
	public void save(String bookname, String name, String phone) {

		AddressBook addrBook = null;

		if (exist(bookname)) {
			addrBook = getAddressBook(bookname);
		} else {
			addrBook = new AddressBook();
		}

		addrBook.add(name, phone);
		addressBooks.put(bookname, addrBook);
	}

	/**
	 * Record a new address book from a CSV file. To do that, this method uses a
	 * CSV parser, and record names and phone number in the CSV file into the
	 * address book.
	 * 
	 * @param file
	 *            of a csv file that contains name and phones
	 * @param id
	 *            that can be used to name an address book
	 * @throws IOException
	 */
	public void save(String file, String id) throws IOException {

		CSVParser csvParser = new CSVParser(file);
		AddressBook addrBook = new AddressBook();

		Entry entry = null;

		while ((entry = csvParser.next()) != null) {
			addrBook.add(entry.getName(), entry.getPhone());
		}
		csvParser.close();
		addressBooks.put(id, addrBook);
	}

	/**
	 * @return names of address books recorded in this application
	 */
	public Set<String> recordedBooks() {
		return addressBooks.keySet();
	}

	/**
	 * @param id
	 *            of an address book recorded in the application
	 * @return an address book that matches the provided id.
	 */
	public AddressBook getAddressBook(String id) {
		return addressBooks.get(id);
	}

	/**
	 * @param id
	 * @return true if the application has recorded an address book with the
	 *         provided id.
	 */
	public boolean exist(String id) {
		return (getAddressBook(id) != null);
	}
}

/**
 * A class that wraps opencsv's CSVReader. This class provides helper methods to
 * retrieve name and phone in a CSV file.
 * 
 * @author Zainul Franciscus
 * 
 */
class CSVParser {

	/**
	 * Used to open an input stream to a CSV file
	 */
	private FileInputStream fileInputStream;
	/**
	 * Used to read a CSV file
	 */
	private InputStreamReader inputStreamReader;
	/**
	 * A CSV Parser
	 */
	private CSVReader csvReader;

	public CSVParser(String file) throws FileNotFoundException, UnsupportedEncodingException {

		fileInputStream = new FileInputStream(new File(file));
		inputStreamReader = new InputStreamReader(fileInputStream, "GB2312");

		csvReader = new CSVReader(inputStreamReader, ',', '\'', 1);
	}

	/**
	 * @return en Entry that contains a name and a phone number from one line of
	 *         the provided CSV file.
	 * @throws IOException
	 */
	public Entry next() throws IOException {
		String[] str = csvReader.readNext();
		Entry entry = null;
		if (str != null) {
			entry = new Entry(str[0], str[1]);
		}
		return entry;
	}

	/**
	 * Close the underlying input stream used to read a csv file.
	 * 
	 * @throws IOException
	 */
	public void close() throws IOException {
		csvReader.close();
	}

	/**
	 * This class is used to record a name and a phone number in a CSV file.
	 * 
	 * @author Zainul Franciscus
	 * 
	 */
	class Entry {
		private String name;
		private String phone;

		Entry(String n, String p) {
			name = n;
			phone = p;
		}

		/**
		 * @return the name
		 */
		public String getName() {
			return name;
		}

		/**
		 * @return the phone
		 */
		public String getPhone() {
			return phone;
		}

	}
}
